var arrError = minErr('ArrayHelper');
var arrayUtil = jc.Array = {
    groupBy: groupBy,
    objToArr: objToArr,
    isEqual: isEqual,
    group: group,
    getIndex: getIndex,
    contain: contain,
    getDistinct: getDistinct,
    replaceElem: replaceElem
};
/**
 * array groupBy function, and oops! the element of array should be
 * Object! we may discard this limitation laterly.
 * @param  {array} array
 * @param  {string} key     it can be "a.b.c"
 * @param  {function=} filter  filter the array element and return boolean ,it receive array element and index params
 * @return {object}        the object is "key:[after filter Array]"
 */
function groupBy(array, key, filter) {
    var rkey, result = {}

    if (filter) {
        array.forEach(function(elem, index) {
            if (filter(elem, index)) {
                rkey = dotPath(elem, key)
                rkey in result ? result[rkey].push(elem) : result[rkey] = [elem]
            }
        })
    } else {
        array.forEach(function(elem, index) {
            rkey = dotPath(elem, key)
            rkey in result ? result[rkey].push(elem) : result[rkey] = [elem]
        })
    }
    return result

}

/**
 *
 * normal group function , group by the groupFn result
 * @param  {array} array
 * @param  {function<elem,index>} groupFn
 * @return {object}
 */
function group(array, groupFn) {
    var result = {}
    array.forEach(function(elem, index) {
        var key = groupFn(elem, index)
        key in result ? result[key].push(elem) : result[key] = [elem]
    })
    return result
}

/**
 * transfer the object to array like this
 * [{key:...,value:...},{key:...,value:...}...]
 * @param  {object} object
 * @param  {string=} keyName   default value is 'key'
 * @param  {string=} valueName default value is 'value'
 * @return {array}
 */
function objToArr(object, keyName, valueName) {
    var key, re = [],
        tempObj;

    keyName = keyName || "key"
    valueName = valueName || "value"
    for (key in object) {
        if (object.hasOwnProperty(key)) {
            tempObj = {}
            tempObj[keyName] = key
            tempObj[valueName] = object[key]
            re.push(tempObj)
        }
    }
    return re;
}

/**
 *  assert the arrays have same element value
 * @param  {array}  arr1
 * @param  {array}  arr2
 * @param  {Boolean=}  deep
 * @return {Boolean}
 */
function isEqual(arr1, arr2, deep) {
    if (arr1.length !== arr2.length) {
        return false
    }
    for (var i = 0; i < arr1.length; i++) {
        if (deep && Array.isArray(arr1[i]) && Array.isArray(arr2[i])) {
            if (!isEqual(arr1[i], arr2[i], deep))
                return false
        } else {
            if (arr1[i] !== arr2[i])
                return false
        }
    }
    return true;
}

/**
 * 根据查询条件获取数组的 index,如果条件都不满足,返回-1
 * @example
 * var arr=[{key:"aa",key2:"bb".....},{key:"abc",key2....}]
 * jc.Array.getIndex(arr,key,"aa")//0
 * @param  {Array<Object>} arr
 * @param  {String|number} key   数组内对象的key
 * @param  {＊} value
 * @return {Number}
 */
function getIndex(arr, key, value) {
    for (var i = 0, elem; elem = arr[i]; i++) {
        if (elem[key] === value) {
            return i
        }
    }
    return -1
}

/**
 * 判定array 数组 是否存在值，支持 的数组格式有
 * ［1，2，3，3，4 ....］
 *  [{key:vlue},{key:value}...]
 *
 * @param  {Array<*>} arr
 * @param  {String=} key
 * @param  {*} value
 * @return {Boolean}
 */
function contain(arr, key, value) {
    var i = 0,
        elem;
    if (arr instanceof Array) {
        if (arguments.length === 2) {
            value = key
            for (; elem = arr[i++];) {
                if (value === elem) {
                    return true
                }
            }
            return false
        }
        while (elem = arr[i++]) {
            if (value === elem[key])
                return true
        }
        return false
    } else {
        arrError("the contain function only accept array param, please check if the first param is an Array")
        //throw new Exception("param invalid")
    }
}
/**
 * 过滤出数组中重复的元素，并返回一个新的不重复的数组，原数组不会被改变,
 * 注意，现在还不支持关于Array<Object>的深度判定，要有这方面需求的话，
 * 可自行扩展 。另外这个实现方法不算效率最高的。
 * @param  {Array} arr
 * @return {Array}
 */
function getDistinct(arr) {
    var distinctArr = []
    if (arr instanceof Array) {
        arr.forEach(function(elem) {
            if (!contain(distinctArr, elem)) {
                distinctArr.push(elem)
            }
        })
    } else {
        arrError("the getDistinct function only accept array param, please check if the first param is an Array")
    }
    return distinctArr
}
/**
 *  这个函数还可以往外扩展一个参数,getIndex函数可以复用到此函数中去
 * @param  {Array} arr
 * @param  {String=} key
 * @param  {*} value
 * @param  {*} newData
 * @return {Boolean}
 */
function replaceElem(arr, key, value, newData) {
    var i;
    if (arguments.length === 3) {
        newData = value
        value = key
        for (i = 0; i < arr.length; i++) {
            if (arr[i] === value) {
                arr[i] = newData
                return true
            }
        }
    } else {
        for (i = 0; i < arr.length; i++) {
            if (arr[i][key] === value) {
                arr[i] = newData
                return true
            }
        }
    }
    return false
}